/*
 * Copyright (C) 2016 Lokiy(liulongke@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  　　　　http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.lokiy.kit.ktx

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.math.BigDecimal
import java.math.BigInteger

/**
 * Anys
 * @author Lokiy
 * 2021-04-27 9:57
 */


/**
 * 转换
 */
@Suppress("IMPLICIT_CAST_TO_ANY")
inline fun <reified T> Any.cast(
    type: Type = object : TypeToken<T>() {}.type,
    def: T? = null,
    noinline block: Any.() -> String = { toString() }
): T? {
    val fromClazz = this::class.java
    val toClazz = T::class.java

    val t: T?
    if (toClazz.isInstance(this)) {
        t = this as T
    } else if (toClazz.isArray && fromClazz.isArray) {
        t = this as T
    } else if (Number::class.java.isAssignableFrom(fromClazz) && Number::class.java.isAssignableFrom(toClazz)) {
        val number = BigDecimal(this.toString())
        t = when (toClazz) {
            Long::class.java -> number.toLong()
            Double::class.java -> number.toDouble()
            Float::class.java -> number.toFloat()
            Int::class.java -> number.toInt()
            BigInteger::class.java -> number.toBigInteger()
            Byte::class.java -> number.toByte()
            Short::class.java -> number.toShort()
            else -> number.toInt()
        } as T
    } else {
        t = try {
            Gson().fromJson(block(this), type)
        } catch (e: Exception) {
            e.printStackTrace()
            def
        }
    }
    return t;
}